﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Lipschitz filter
    ///  is a strong form of uniform continuity for functions.
    ///  Intuitively, a Lipschitz continuous function is limited in how fast it can change:
    ///  For every pair of points on the graph of this function, 
    ///  their secant line-segment's slope has absolute-value no greater than a definite real number; 
    ///  this constant, which is the same for all the secants, is called the function's "Lipschitz constant" 
    ///  (or "modulus of uniform continuity").
    /// </summary>
    public class Lipschitz : BaseImageFilter
    {
        // the following are the input parameters, with default values assigned to them
        protected bool m_Down = true;     //  
        protected bool m_TopHat = false;  // lower Lipschitz cover  
        protected double m_Slope = 10;                 // slope

        //protected ImagePlus m_imp;
        protected int m_scount;                      // number of stacks
        //protected ImageStack m_stack, m_stack_out;

        protected static int m_nImageHeight = -1;
        protected static int m_nImageWidth = -1;
        protected static int m_nChannels = 3;
        protected static bool breaked = false;
        protected static bool m_bShort = false;

        //static ImageProcessor iptmp;
        int[] m_aPixel;

        /// <summary>
        /// Initializes a new instance of the <see cref="Lipschitz"/> class.
        /// </summary>
        public Lipschitz()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Lipschitz"/> class.
        /// </summary>
        internal Lipschitz(Lipschitz _lipSchitz)
        {
            m_Down = _lipSchitz.m_Down;
            m_TopHat = _lipSchitz.m_TopHat;
        }

        /// <summary>
        /// Gets or sets a value indicating whether [top hat].
        /// </summary>
        /// <value><c>true</c> if [top hat]; otherwise, <c>false</c>.</value>
        public bool TopHat
        {
            get { return m_TopHat; }
            set { m_TopHat = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="Lipschitz"/> is down.
        /// </summary>
        /// <value><c>true</c> if down; otherwise, <c>false</c>.</value>
        public bool Down
        {
            get { return m_Down; }
            set { m_Down = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int nSlope, nSlope1, p = 0, p1 = 0, p2 = 0, p3 = 0, p4 = 0, nMaxz = 0;

            int nIndex = 0;
            int x = 0;
            int y = 0;
            int z = 0;

            m_nImageHeight = _bitmap.Height;
            m_nImageWidth = _bitmap.Width;

            int nPixelCount = m_nImageHeight * m_nImageWidth;

            UnsafeBitmap destPixels = _bitmap;
            UnsafeBitmap srcPixels = (UnsafeBitmap)_bitmap.Clone();
            UnsafeBitmap tmpBytePixels = (UnsafeBitmap)_bitmap.Clone();
            UnsafeBitmap tmpShortPixels = (UnsafeBitmap)_bitmap.Clone();

            int sign = (m_Down ? 1 : -1);
            int topdown = (m_Down ? 0 : 255);

            for (int ii = 0; ii < m_nChannels; ii++)
            {
                for (y = 0; y < m_nImageHeight; y++)
                {
                    for (x = 0; x < m_nImageWidth; x++)
                    {
                        srcPixels[x, y, ii] = (byte)(sign * (tmpBytePixels[x, y, ii] & 0xff));
                        destPixels[x, y, ii] = srcPixels[x, y, ii];
                    }
                }
            }

            nSlope = (int)(m_Slope);
            nSlope1 = (int)(nSlope * Math.Sqrt(2.0));
            nMaxz = m_nChannels;

            for (y = 0; y < m_nImageHeight; y++)   // rows
            {
                for (z = 0; z < m_nChannels; z++)
                {
                    p2 = sign * (topdown + (sign) * nSlope);
                    p3 = sign * (topdown + (sign) * nSlope1);
                    for (x = 0; x < m_nImageWidth; x++) // columns
                    {
                        p = (p2 - nSlope);
                        p1 = (p3 - nSlope1);
                        if (p1 > p) p = p1;
                        p3 = destPixels[x, (Math.Max(y - 1, 0)), z];
                        p1 = p3 - nSlope;
                        if (p1 > p) p = p1;

                        p4 = destPixels[Math.Min(x + 1, m_nImageWidth - 1), (Math.Max(y - 1, 0)), z];
                        p1 = p4 - nSlope1;
                        if (p1 > p) p = p1;

                        p2 = srcPixels[x, y, z];
                        if (p > p2)
                        {
                            destPixels[x, y, z] = (byte)p;
                            p2 = p;
                        }
                    }
                }
            }

            for (y = m_nImageHeight - 1; y >= 0; y--)
            {
                for (z = 0; z < nMaxz; z++)
                {
                    p2 = sign * (topdown + (sign) * nSlope);
                    p3 = sign * (topdown + (sign) * nSlope1);
                    for (x = m_nImageWidth - 1; x >= 0; x--)
                    {
                        p = (p2 - nSlope);
                        p1 = (p3 - nSlope1);
                        if (p1 > p) p = p1;

                        p3 = destPixels[x, (Math.Min(y + 1, m_nImageHeight - 1)), z];
                        p1 = p3 - nSlope;
                        if (p1 > p) p = p1;

                        p4 = destPixels[Math.Max(x - 1, 0), (Math.Min(y + 1, m_nImageHeight - 1)), z];
                        p1 = p4 - nSlope1;
                        if (p1 > p) p = p1;

                        p2 = destPixels[x, y, z];
                        if (p > p2)
                        {
                            destPixels[x, y, z] = (byte)p;
                            p2 = p;
                        }
                    }
                }
            }

            x = 0;
            y = 0;
            for (int ii = 0; ii < m_nChannels; ii++)
            {
                for (y = 0; y < m_nImageHeight; y++)
                {
                    for (x = 0; x < m_nImageWidth; x++)
                    {
                        if (m_TopHat)
                        {
                            tmpBytePixels[x, y, ii] = (m_Down ? (byte)(srcPixels[x, y, ii] - destPixels[x, y, ii] + 255)
                           : (byte)(destPixels[x, y, ii] - srcPixels[x, y, ii]));
                        }
                        else
                        {
                            tmpBytePixels[x, y, ii] = (byte)(sign * destPixels[x, y, ii]);
                        }
                        nIndex = 0;
                    }
                }
            }

            if (m_nChannels == 1)
            {
                if (m_bShort)
                {
                    BitmapFunctions.Copy(tmpShortPixels, _bitmap);
                }
                else
                {
                    BitmapFunctions.Copy(tmpBytePixels, _bitmap);
                }

            }
            else
            {
                BitmapFunctions.Copy(tmpBytePixels, _bitmap);
            } 
            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Lipschitz(this);
        }
    }
}
